package mx.com.crowdgine.crowds.samples;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import javax.media.opengl.GLAutoDrawable;

import org.w3c.dom.Node;

import com.jogamp.common.nio.Buffers;
import com.jogamp.opencl.CLBuffer;
import com.jogamp.opencl.gl.CLGLBuffer;

import mx.com.crowdgine.assetloader.Asset;
import mx.com.crowdgine.crowds.CrowdProperties;
import mx.com.crowdgine.crowds.CrowdSystem;
import mx.com.crowdgine.display.jogl.FrameBuffer;
import mx.com.crowdgine.display.jogl.shader.ShaderObject;
import mx.com.crowdgine.util.gpucompute.jocl.JoclGpuCompute;
import mx.com.crowdgine.util.xml.XMLReader;

public class GenericCrowds implements CrowdSystem, CrowdProperties{
	
	private static final int BLOCK_SIZE = 16;
	
	private List<Asset> assets = new ArrayList<Asset>();
	private FrameBuffer frameBuffer = new FrameBuffer();
	private ShaderObject crowdPosShader;
	private ShaderObject particleShader;
	private Asset asset;
	
	//Kernel files
	private final String KERNEL_FILE_MOVEMENT = "BasicMovementKernel.cl";
	private final String KERNEL_FILE_SORT = "MatrixSortKernel.cl";
	private final String KERNEL_FILE_INIT = "UniformInitCrowdKernel.cl";
	private final String KERNEL_FILE_MESSAGE = "MessagesKernel.cl";
	private final String KERNEL_FILE_INIT_TEXTURE = "TextureInitKernel";
	
	//Kernel names
	private final String KERNEL_INIT = "initCrowd";
	private final String KERNEL_MOVEMENT = "basicMovementKernel";
	private final String KERNEL_SORT_EVEN_COLUMNS = "sortEvenColumns";
	private final String KERNEL_SORT_ODD_COLUMNS = "sortOddColumns";
	private final String KERNEL_SORT_EVEN_ROWS = "sortEvenRows";
	private final String KERNEL_SORT_ODD_ROWS = "sortOddRows";
	private final String KERNEL_MESSAGE = "messageKernel";
	
	//Buffers to use
	public static final String AGENTS_NAME = "agents";
	public static final String VELOCITY = "velocity";
	public static final String MAX_VELOCITY = "maxVel";
	public static final String MAX_ACCELERATION = "maxAccel";
	public static final String LEADER_MOVEMENT = "leaderMovement";
	public static final String PARTICLE_MOVEMENT = "particleMovement";
	public static final String FLOCKING = "flocking";
	public static final String GROUPING_OPTIONS = "groupingOptions";
	public static final String MOVEMENT_OPTIONS = "movementOptions";
	public static final String GROUP_DEFINITION = "groupDefinition";
	public static final String ORIGINAL_ID = "originalID";
	public static final String MESSAGE = "message";
	public static final String ROAD_MAP = "roadMap";

	private boolean verbose = false;
	private boolean initialized = false;
	protected JoclGpuCompute crowd;
	
	//Init variables for the crowd
	private int crowdSize = 0;
	private float groupDefinition[];
	private LinkedList<Float> leaderMovement;
	private LinkedList<Float> particleMovement;
	private float crowdFlocking[];
	private int neighborhoodRadius;
	private float socialBehavior[];
	private float diversity[];
	
	public GenericCrowds(Node crowds, boolean... verbose){
		//set debug property
		if(verbose.length > 0)
			this.verbose = verbose[0];
		
		LinkedList<HashMap<String, String>> crowdsInfo = XMLReader.getNodeInformation(crowds);
		
		//Init crowd size, default = 16 * 16
		String size = crowdsInfo.get(0).get(XMLReader.CROWDS_SIZE);
		if(size != null){
			crowdSize = Integer.parseInt(size);
			if(crowdSize < 16)
				crowdSize = 16;
		}else{
			crowdSize = 256;
		}
		
		//Get the tags inside the crowds node
		HashMap<String, Node> crowdsHash = XMLReader.buildHashMap(crowds);
		
		//Init Crowd properties
		setCrowdBasicMovements(crowdsHash);
		setCrowdGroupAttributes(crowdsHash);
		setCrowdSocialBehaviors(crowdsHash);
		setCrowdDiversity(crowdsHash);
		
		if(this.verbose)
			printStatus();
	}
	
	public GenericCrowds(Node crowds, Node places){
		/*setCrowdBasicMovements(crowdsHash);
		setCrowdGroupAttributes(crowdsHash);
		setCrowdPreferences(crowdsHash);
		setCrowdSocialBehaviors(crowdsHash);
		setCrowdDiversity(crowdsHash);*/
	}
	
	public GenericCrowds(int crowdSize, boolean... verbose){
		//set debug property
		if(verbose.length > 0)
			this.verbose = verbose[0];
		
		this.crowdSize = crowdSize;
		if(this.crowdSize < 256)
			this.crowdSize = 256;
		
		/*setCrowdBasicMovements(null);
		setCrowdGroupAttributes(null);
		setCrowdSocialBehaviors(null);
		setCrowdDiversity(null);*/
	}
	
	private void printStatus(){
		System.out.println("Crowd size: " + crowdSize);
		
		System.out.print("Group definition: ");
		for(int i=0; i<groupDefinition.length; i++)
			System.out.print(groupDefinition[i] + " ");
		System.out.println();
		
		System.out.print("Leader Movement: ");
		for(int i=0; i<leaderMovement.size(); i++)
			System.out.print(leaderMovement.get(i) + " ");
		System.out.println();
		
		System.out.print("Particle Movement: ");
		for(int i=0; i<particleMovement.size(); i++)
			System.out.print(particleMovement.get(i) + " ");
		System.out.println();
		
		System.out.print("Crowd flocking: ");
		for(int i=0; i<crowdFlocking.length; i++)
			System.out.print(crowdFlocking[i] + " ");
		System.out.println();
		
		System.out.println("Radius : " + neighborhoodRadius);
		
		System.out.print("Social behaviors: ");
		for(int i=0; i<socialBehavior.length; i++)
			System.out.print(socialBehavior[i] + " ");
		System.out.println();
		
		System.out.print("Diversity: ");
		for(int i=0; i<diversity.length; i++)
			System.out.print(diversity[i] + " ");
		System.out.println();
	}
	
	@Override
	public void initCrowd(Object drawable) {
		if(drawable instanceof GLAutoDrawable) {
			crowd  = new JoclGpuCompute((GLAutoDrawable)drawable, verbose);
			crowd.initCL(this.getClass().getResourceAsStream(KERNEL_FILE_MOVEMENT));
			crowd.addKernelFile(this.getClass().getResourceAsStream(KERNEL_FILE_SORT));
			crowd.addKernelFile(this.getClass().getResourceAsStream(KERNEL_FILE_INIT));
			crowd.addKernelFile(this.getClass().getResourceAsStream(KERNEL_FILE_MESSAGE));
		
			//Initialize Buffers
			int size = Buffers.SIZEOF_FLOAT * 4 * crowdSize * crowdSize;
			crowd.createCLGLBuffer((GLAutoDrawable)drawable, AGENTS_NAME, size, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(VELOCITY, size, CLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(MAX_VELOCITY, size/4, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(MAX_ACCELERATION, size/4, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(LEADER_MOVEMENT, leaderMovement.size() * Buffers.SIZEOF_FLOAT, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(PARTICLE_MOVEMENT, particleMovement.size() * Buffers.SIZEOF_FLOAT, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(FLOCKING, crowdFlocking.length * Buffers.SIZEOF_FLOAT, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(GROUPING_OPTIONS, size, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(MOVEMENT_OPTIONS, size, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(GROUP_DEFINITION, groupDefinition.length * Buffers.SIZEOF_FLOAT, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(ORIGINAL_ID, size/2, CLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(MESSAGE, size, CLBuffer.Mem.WRITE_ONLY);
			
			Float[] a = new Float[1];
			crowd.fillBuffer(LEADER_MOVEMENT, leaderMovement.toArray(a));
			crowd.fillBuffer(PARTICLE_MOVEMENT, particleMovement.toArray(a));
			crowd.fillBuffer(FLOCKING, crowdFlocking);
			crowd.fillBuffer(GROUP_DEFINITION, groupDefinition);
		
			/*crowd.setKernelArg(KERNEL_INIT, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_INIT, 1, crowd.clBuffers.get(VELOCITY));
			crowd.setKernelArg(KERNEL_INIT, 2, crowd.clBuffers.get(ORIGINAL_ID));
			crowd.setKernelArg(KERNEL_INIT, 3, crowdSize);*/
			
			crowd.setKernelArg(KERNEL_INIT, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_INIT, 1, crowd.clBuffers.get(VELOCITY));
			crowd.setKernelArg(KERNEL_INIT, 2, crowd.clBuffers.get(MAX_VELOCITY));
			crowd.setKernelArg(KERNEL_INIT, 3, crowd.clBuffers.get(MAX_ACCELERATION));
			crowd.setKernelArg(KERNEL_INIT, 4, crowd.clBuffers.get(LEADER_MOVEMENT));
			crowd.setKernelArg(KERNEL_INIT, 5, leaderMovement.size()/5);
			crowd.setKernelArg(KERNEL_INIT, 6, crowd.clBuffers.get(PARTICLE_MOVEMENT));
			crowd.setKernelArg(KERNEL_INIT, 7, particleMovement.size()/5);
			crowd.setKernelArg(KERNEL_INIT, 8, crowd.clBuffers.get(FLOCKING));
			crowd.setKernelArg(KERNEL_INIT, 9, crowd.clBuffers.get(GROUPING_OPTIONS));
			crowd.setKernelArg(KERNEL_INIT, 10, crowd.clBuffers.get(MOVEMENT_OPTIONS));
			crowd.setKernelArg(KERNEL_INIT, 11, crowd.clBuffers.get(GROUP_DEFINITION));
			crowd.setKernelArg(KERNEL_INIT, 12, groupDefinition.length/2);
			crowd.setKernelArg(KERNEL_INIT, 13, crowd.clBuffers.get(ORIGINAL_ID));
			crowd.setKernelArg(KERNEL_INIT, 14, crowdSize);
			crowd.setKernelArg(KERNEL_INIT, 15, 640);
			crowd.setKernelArg(KERNEL_INIT, 16, 480);
		
			crowd.commandQueue.putAcquireGLObject(crowd.clGLBuffers.get(AGENTS_NAME).ID)
								.put2DRangeKernel(crowd.kernels.get(KERNEL_INIT), 0, 0, crowdSize, crowdSize, 0, 0)
								.putReleaseGLObject(crowd.clGLBuffers.get(AGENTS_NAME).ID)
								.finish();
			
			/*crowd.releaseCLGLBuffer(LEADER_MOVEMENT);
			crowd.releaseCLGLBuffer(PARTICLE_MOVEMENT);
			crowd.releaseCLGLBuffer(FLOCKING);
			crowd.releaseCLGLBuffer(GROUP_DEFINITION);*/
			
			/*crowd.setKernelArg(KERNEL_MOVEMENT, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_MOVEMENT, 1, crowd.clBuffers.get(VELOCITY));
			crowd.setKernelArg(KERNEL_MOVEMENT, 2, crowd.clBuffers.get(ORIGINAL_ID));
			crowd.setKernelArg(KERNEL_MOVEMENT, 3, crowdSize);
			crowd.setKernelArg(KERNEL_MOVEMENT, 4, 0.0f);*/
			
			crowd.setKernelArg(KERNEL_MESSAGE, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_MESSAGE, 1, crowd.clBuffers.get(GROUPING_OPTIONS));
			crowd.setKernelArg(KERNEL_MESSAGE, 2, crowd.clBuffers.get(MESSAGE));
			crowd.setKernelArg(KERNEL_MESSAGE, 3, crowd.clBuffers.get(ORIGINAL_ID));
			crowd.setKernelArg(KERNEL_MESSAGE, 4, crowdSize);
			
			crowd.setKernelArg(KERNEL_MOVEMENT, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_MOVEMENT, 1, crowd.clBuffers.get(VELOCITY));
			crowd.setKernelArg(KERNEL_MOVEMENT, 2, crowd.clBuffers.get(MAX_VELOCITY));
			crowd.setKernelArg(KERNEL_MOVEMENT, 3, crowd.clBuffers.get(MAX_ACCELERATION));
			crowd.setKernelArg(KERNEL_MOVEMENT, 4, crowd.clBuffers.get(GROUPING_OPTIONS));
			crowd.setKernelArg(KERNEL_MOVEMENT, 5, crowd.clBuffers.get(MOVEMENT_OPTIONS));
			crowd.setKernelArg(KERNEL_MOVEMENT, 6, crowd.clBuffers.get(MESSAGE));
			crowd.setKernelArg(KERNEL_MOVEMENT, 7, crowd.clBuffers.get(ORIGINAL_ID));
			crowd.setKernelArg(KERNEL_MOVEMENT, 8, crowdSize);
			crowd.setKernelArg(KERNEL_MOVEMENT, 9, 0.0f);
			
			crowd.setKernelArg(KERNEL_SORT_EVEN_COLUMNS, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_SORT_EVEN_COLUMNS, 1, crowd.clBuffers.get(ORIGINAL_ID));
			crowd.setKernelArg(KERNEL_SORT_EVEN_COLUMNS, 2, crowdSize);
			
			crowd.setKernelArg(KERNEL_SORT_ODD_COLUMNS, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_SORT_ODD_COLUMNS, 1, crowd.clBuffers.get(ORIGINAL_ID));
			crowd.setKernelArg(KERNEL_SORT_ODD_COLUMNS, 2, crowdSize);
			
			crowd.setKernelArg(KERNEL_SORT_EVEN_ROWS, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_SORT_EVEN_ROWS, 1, crowd.clBuffers.get(ORIGINAL_ID));
			crowd.setKernelArg(KERNEL_SORT_EVEN_ROWS, 2, crowdSize);
			
			crowd.setKernelArg(KERNEL_SORT_ODD_ROWS, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_SORT_ODD_ROWS, 1, crowd.clBuffers.get(ORIGINAL_ID));
			crowd.setKernelArg(KERNEL_SORT_ODD_ROWS, 2, crowdSize);
			
			initialize(drawable);
			
			initialized = true;
		} else {
			throw new UnsupportedOperationException("GenericCrowds: Drawable implementation " + drawable + " not currently supported.");
		}
	}
	
	public void initCrowd(Object drawable, float[] roadMap, int roadMapWidth, int roadMapHeight, int scale) {
		if(drawable instanceof GLAutoDrawable) {
			crowd  = new JoclGpuCompute((GLAutoDrawable)drawable, verbose);
			crowd.initCL(this.getClass().getResourceAsStream(KERNEL_FILE_MOVEMENT));
			crowd.addKernelFile(this.getClass().getResourceAsStream(KERNEL_FILE_SORT));
			crowd.addKernelFile(this.getClass().getResourceAsStream(KERNEL_FILE_INIT_TEXTURE));
			crowd.addKernelFile(this.getClass().getResourceAsStream(KERNEL_FILE_MESSAGE));
		
			//Initialize Buffers
			int size = Buffers.SIZEOF_FLOAT * 4 * crowdSize * crowdSize;
			crowd.createCLGLBuffer((GLAutoDrawable)drawable, AGENTS_NAME, size, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(VELOCITY, size, CLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(MAX_VELOCITY, size/4, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(MAX_ACCELERATION, size/4, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(LEADER_MOVEMENT, leaderMovement.size() * Buffers.SIZEOF_FLOAT, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(PARTICLE_MOVEMENT, particleMovement.size() * Buffers.SIZEOF_FLOAT, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(FLOCKING, crowdFlocking.length * Buffers.SIZEOF_FLOAT, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(GROUPING_OPTIONS, size, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(MOVEMENT_OPTIONS, size, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(GROUP_DEFINITION, groupDefinition.length * Buffers.SIZEOF_FLOAT, CLGLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(ORIGINAL_ID, size/2, CLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(MESSAGE, size, CLBuffer.Mem.WRITE_ONLY);
			crowd.createCLGLBuffer(ROAD_MAP, Buffers.SIZEOF_FLOAT * 4 * roadMapWidth * roadMapHeight, CLBuffer.Mem.WRITE_ONLY);
			
			Float[] a = new Float[1];
			crowd.fillBuffer(LEADER_MOVEMENT, leaderMovement.toArray(a));
			crowd.fillBuffer(PARTICLE_MOVEMENT, particleMovement.toArray(a));
			crowd.fillBuffer(FLOCKING, crowdFlocking);
			crowd.fillBuffer(GROUP_DEFINITION, groupDefinition);
			crowd.fillBuffer(ROAD_MAP, roadMap);
			
			crowd.setKernelArg(KERNEL_INIT, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_INIT, 1, crowd.clBuffers.get(VELOCITY));
			crowd.setKernelArg(KERNEL_INIT, 2, crowd.clBuffers.get(MAX_VELOCITY));
			crowd.setKernelArg(KERNEL_INIT, 3, crowd.clBuffers.get(MAX_ACCELERATION));
			crowd.setKernelArg(KERNEL_INIT, 4, crowd.clBuffers.get(LEADER_MOVEMENT));
			crowd.setKernelArg(KERNEL_INIT, 5, leaderMovement.size()/5);
			crowd.setKernelArg(KERNEL_INIT, 6, crowd.clBuffers.get(PARTICLE_MOVEMENT));
			crowd.setKernelArg(KERNEL_INIT, 7, particleMovement.size()/5);
			crowd.setKernelArg(KERNEL_INIT, 8, crowd.clBuffers.get(FLOCKING));
			crowd.setKernelArg(KERNEL_INIT, 9, crowd.clBuffers.get(GROUPING_OPTIONS));
			crowd.setKernelArg(KERNEL_INIT, 10, crowd.clBuffers.get(MOVEMENT_OPTIONS));
			crowd.setKernelArg(KERNEL_INIT, 11, crowd.clBuffers.get(GROUP_DEFINITION));
			crowd.setKernelArg(KERNEL_INIT, 12, groupDefinition.length/2);
			crowd.setKernelArg(KERNEL_INIT, 13, crowd.clBuffers.get(ORIGINAL_ID));
			crowd.setKernelArg(KERNEL_INIT, 14, crowdSize);
			crowd.setKernelArg(KERNEL_INIT, 15, crowd.clBuffers.get(ROAD_MAP));
			crowd.setKernelArg(KERNEL_INIT, 16, scale);
			crowd.setKernelArg(KERNEL_INIT, 17, roadMapWidth);
			crowd.setKernelArg(KERNEL_INIT, 18, roadMapHeight);
		
			crowd.commandQueue.putAcquireGLObject(crowd.clGLBuffers.get(AGENTS_NAME).ID)
								.put2DRangeKernel(crowd.kernels.get(KERNEL_INIT), 0, 0, crowdSize, crowdSize, 16, 16)
								.putReleaseGLObject(crowd.clGLBuffers.get(AGENTS_NAME).ID)
								.finish();
			
			/*crowd.releaseCLGLBuffer(LEADER_MOVEMENT);
			crowd.releaseCLGLBuffer(PARTICLE_MOVEMENT);
			crowd.releaseCLGLBuffer(FLOCKING);
			crowd.releaseCLGLBuffer(GROUP_DEFINITION);*/
			
			
			crowd.setKernelArg(KERNEL_MESSAGE, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_MESSAGE, 1, crowd.clBuffers.get(GROUPING_OPTIONS));
			crowd.setKernelArg(KERNEL_MESSAGE, 2, crowd.clBuffers.get(MESSAGE));
			crowd.setKernelArg(KERNEL_MESSAGE, 3, crowd.clBuffers.get(ORIGINAL_ID));
			crowd.setKernelArg(KERNEL_MESSAGE, 4, crowdSize);
			
			crowd.setKernelArg(KERNEL_MOVEMENT, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_MOVEMENT, 1, crowd.clBuffers.get(VELOCITY));
			crowd.setKernelArg(KERNEL_MOVEMENT, 2, crowd.clBuffers.get(MAX_VELOCITY));
			crowd.setKernelArg(KERNEL_MOVEMENT, 3, crowd.clBuffers.get(MAX_ACCELERATION));
			crowd.setKernelArg(KERNEL_MOVEMENT, 4, crowd.clBuffers.get(GROUPING_OPTIONS));
			crowd.setKernelArg(KERNEL_MOVEMENT, 5, crowd.clBuffers.get(MOVEMENT_OPTIONS));
			crowd.setKernelArg(KERNEL_MOVEMENT, 6, crowd.clBuffers.get(MESSAGE));
			crowd.setKernelArg(KERNEL_MOVEMENT, 7, crowd.clBuffers.get(ORIGINAL_ID));
			crowd.setKernelArg(KERNEL_MOVEMENT, 8, crowdSize);
			crowd.setKernelArg(KERNEL_MOVEMENT, 9, 0.0f);
			
			crowd.setKernelArg(KERNEL_SORT_EVEN_COLUMNS, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_SORT_EVEN_COLUMNS, 1, crowd.clBuffers.get(ORIGINAL_ID));
			crowd.setKernelArg(KERNEL_SORT_EVEN_COLUMNS, 2, crowdSize);
			
			crowd.setKernelArg(KERNEL_SORT_ODD_COLUMNS, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_SORT_ODD_COLUMNS, 1, crowd.clBuffers.get(ORIGINAL_ID));
			crowd.setKernelArg(KERNEL_SORT_ODD_COLUMNS, 2, crowdSize);
			crowd.setKernelArg(KERNEL_SORT_EVEN_ROWS, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_SORT_EVEN_ROWS, 1, crowd.clBuffers.get(ORIGINAL_ID));
			crowd.setKernelArg(KERNEL_SORT_EVEN_ROWS, 2, crowdSize);
			
			crowd.setKernelArg(KERNEL_SORT_ODD_ROWS, 0, crowd.clGLBuffers.get(AGENTS_NAME));
			crowd.setKernelArg(KERNEL_SORT_ODD_ROWS, 1, crowd.clBuffers.get(ORIGINAL_ID));
			crowd.setKernelArg(KERNEL_SORT_ODD_ROWS, 2, crowdSize);
			
			initialize(drawable);
			
			initialized = true;
		} else {
			throw new UnsupportedOperationException("GenericCrowds: Drawable implementation " + drawable + " not currently supported.");
		}
	}
	
	public void initialize(Object drawable){
	}
	
	public void updateCrowd(float dt){
		/*crowd.setKernelArg(KERNEL_MOVEMENT, 9, dt);
		
		crowd.commandQueue.putAcquireGLObject(crowd.clGLBuffers.get(AGENTS_NAME).ID);
		
		crowd.commandQueue.put2DRangeKernel(crowd.kernels.get(KERNEL_MESSAGE), 0, 0, crowdSize, crowdSize, BLOCK_SIZE, BLOCK_SIZE)
		.finish();
		
		crowd.commandQueue.put2DRangeKernel(crowd.kernels.get(KERNEL_MOVEMENT), 0, 0, crowdSize, crowdSize, BLOCK_SIZE, BLOCK_SIZE)
		.finish();
		
		
		//Sort Position matrix
		crowd.commandQueue
		.put2DRangeKernel(crowd.kernels.get(KERNEL_SORT_EVEN_COLUMNS), 0, 0, crowdSize, crowdSize, BLOCK_SIZE, BLOCK_SIZE)
		.finish();
		crowd.commandQueue
		.put2DRangeKernel(crowd.kernels.get(KERNEL_SORT_ODD_COLUMNS), 0, 0, crowdSize, crowdSize, BLOCK_SIZE, BLOCK_SIZE)
		.finish();
		crowd.commandQueue
		.put2DRangeKernel(crowd.kernels.get(KERNEL_SORT_EVEN_ROWS), 0, 0, crowdSize, crowdSize, BLOCK_SIZE, BLOCK_SIZE)
		.finish();
		crowd.commandQueue
		.put2DRangeKernel(crowd.kernels.get(KERNEL_SORT_ODD_ROWS), 0, 0, crowdSize, crowdSize, BLOCK_SIZE, BLOCK_SIZE)
		.finish();

		crowd.commandQueue.putReleaseGLObject(crowd.clGLBuffers.get(AGENTS_NAME).ID);

		update(dt);*/
	}

	public void update(float dt){
	}


	@Override
	public void setCrowdBasicMovements(HashMap<String, Node> crowdsHash) {
		//Init movement lists
		this.leaderMovement = new LinkedList<Float>();
		this.particleMovement = new LinkedList<Float>();
		
		//
		boolean leaderMovement = false;
		boolean particleMovement = false;
		boolean flock = false;
		boolean neighborhoodRadius = false;
		
		//Check if there is a basic_movment tag
		Node basicMovement = null;
		if(crowdsHash != null)
			basicMovement = crowdsHash.get(XMLReader.CROWDS_MOVEMENT);
		
		if(basicMovement != null){
			LinkedList<String> movementTags = XMLReader.buildList(basicMovement);
			LinkedList<HashMap<String, String>> movementInfo = XMLReader.getNodeInformation(basicMovement);
			
			for(int i=0; i<movementInfo.size(); i++){
				HashMap<String, String> element = movementInfo.get(i);
				String tag = null;
				for(int j=0; j<movementTags.size(); j++){
					if(element.containsKey(movementTags.get(j))){
						tag = movementTags.get(j);
						movementTags.remove(j);
						break;
					}
				}
				
				if(tag == "flock"){
					flock = true;
					setFlock(element);
					continue;
				}
				
				if(tag == "neighborhood"){
					neighborhoodRadius = true;
					setNeighborhood(element);
					continue;
				}
				
				boolean isLeader = setMovement(element);
				if(isLeader)
					leaderMovement = true;
				else
					particleMovement = true;
				
			}
		}
		
		if(!leaderMovement){
			this.leaderMovement.add(10.0f);
			this.leaderMovement.add(3.0f);
			this.leaderMovement.add(3.0f);
			this.leaderMovement.add(0.0f);
			this.leaderMovement.add(1.0f);
		}
		
		if(!particleMovement){
			this.particleMovement.add(10.0f);
			this.particleMovement.add(2.0f);
			this.particleMovement.add(2.0f);
			this.particleMovement.add(1.0f);
			this.particleMovement.add(0.0f);
		}
		
		if(!flock){
			crowdFlocking = new float[3];
			crowdFlocking[0] = 1.0f;
			crowdFlocking[1] = 1.0f;
			crowdFlocking[2] = 1.0f;
		}
		
		if(!neighborhoodRadius)
			this.neighborhoodRadius = 1;
	}
	
	private void setNeighborhood(HashMap<String, String> radius){
		if(radius.containsKey("radius")){
			neighborhoodRadius = Integer.parseInt(radius.get("radius"));
		}else{
			neighborhoodRadius = 1;
		}
	}
	
	private void setFlock(HashMap<String, String> flock){
		crowdFlocking = new float[3];
		if(flock.containsKey("separation")){
			if(flock.get("separation").equals("0"))
				crowdFlocking[0] = 0.0f;
			else
				crowdFlocking[0] = 1.0f;
		}else{
			crowdFlocking[0] = 1.0f;
		}
		
		if(flock.containsKey("cohesion")){
			if(flock.get("cohesion").equals("0"))
				crowdFlocking[1] = 0.0f;
			else
				crowdFlocking[1] = 1.0f;
		}else{
			crowdFlocking[1] = 1.0f;
		}
		
		if(flock.containsKey("alignment")){
			if(flock.get("alignment").equals("0"))
				crowdFlocking[2] = 0.0f;
			else
				crowdFlocking[2] = 1.0f;
		}else{
			crowdFlocking[2] = 1.0f;
		}
		
	}
	
	private boolean setMovement(HashMap<String, String> particle){
		boolean isLeader = false;
		if(particle.containsKey("leader")){
			isLeader = Boolean.parseBoolean(particle.get("leader"));
		}
		
		if(particle.containsKey("mean_vel")){
			if(isLeader)
				leaderMovement.add((Float.parseFloat(particle.get("mean_vel"))));
			else
				particleMovement.add((Float.parseFloat(particle.get("mean_vel"))));
		}else{
			if(isLeader)
				leaderMovement.add((10.0f));
			else
				particleMovement.add((8.0f));
		}
		
		if(particle.containsKey("std_dv_vel")){
			if(isLeader)
				leaderMovement.add((Float.parseFloat(particle.get("std_dv_vel"))));
			else
				particleMovement.add((Float.parseFloat(particle.get("std_dv_vel"))));
		}else{
			if(isLeader)
				leaderMovement.add((3.0f));
			else
				particleMovement.add((2.0f));
		}
		
		if(particle.containsKey("max_accel")){
			if(isLeader)
				leaderMovement.add((Float.parseFloat(particle.get("max_accel"))));
			else
				particleMovement.add((Float.parseFloat(particle.get("max_accel"))));
		}else{
			if(isLeader)
				leaderMovement.add((3.0f));
			else
				particleMovement.add((2.0f));
		}
		
		if(particle.containsKey("seek")){
			if(isLeader)
				leaderMovement.add((Float.parseFloat(particle.get("seek"))));
			else
				particleMovement.add((Float.parseFloat(particle.get("seek"))));
		}else{
			if(isLeader)
				leaderMovement.add((0.0f));
			else
				particleMovement.add((1.0f));
		}
		
		if(particle.containsKey("wander")){
			if(isLeader)
				leaderMovement.add((Float.parseFloat(particle.get("wander"))));
			else
				particleMovement.add((Float.parseFloat(particle.get("wander"))));
		}else{
			if(isLeader)
				leaderMovement.add((1.0f));
			else
				particleMovement.add((0.0f));
		}
		return isLeader;
	}

	@Override
	public void setCrowdGroupAttributes(HashMap<String, Node> crowdsHash) {
		//Check if there is a groups tag
		Node groups = null;
		if(crowdsHash != null)
			groups = crowdsHash.get(XMLReader.CROWDS_GROUPS);
		
		if(groups != null){
			LinkedList<HashMap<String, String>> crowdsInfo = XMLReader.getNodeInformation(groups);
			int numDefGroups = crowdsInfo.size();
			groupDefinition = new float[numDefGroups * 2];
			int index = 0;
			for(int i=0; i<numDefGroups; i++){
				String size = crowdsInfo.get(i).get("size");
				String occurrence = crowdsInfo.get(i).get("occurrence");
				
				if(size != null && occurrence != null){
					groupDefinition[index] = Integer.parseInt(size);
					index++;
					groupDefinition[index] = Integer.parseInt(occurrence);
					index++;
				}else{
					groupDefinition = new float[2*2];
					groupDefinition[0] = 2;
					groupDefinition[1] = -50;
					groupDefinition[2] = 3;
					groupDefinition[3] = -50;
				}
			}
		}else{
			groupDefinition = new float[2 * 2];
			groupDefinition[0] = 2;
			groupDefinition[1] = -50;
			groupDefinition[2] = 3;
			groupDefinition[3] = -50;
		}
	}

	@Override
	public void setCrowdPreferences(HashMap<String, Node> crowdsHash) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void setCrowdSocialBehaviors(HashMap<String, Node> crowdsHash) {
		//Init the social behavior array
		this.socialBehavior = new float[3];
		
		//
		boolean hasPassive = false;
		boolean hasSocial = false;
		boolean hasAgressive = false;
		
		//Check if there is a social_behavior tag
		Node socialBehavior = null;
		if(crowdsHash != null)
			socialBehavior = crowdsHash.get(XMLReader.CROWDS_SOCIAL);
		
		if(socialBehavior != null){
			LinkedList<HashMap<String, String>> socialInfo = XMLReader.getNodeInformation(socialBehavior);
			for(int i=0; i<socialInfo.size(); i++){
				HashMap<String, String> element = socialInfo.get(i);
				String name = element.get("name");
				String occurrence = element.get("occurrence");
				
				if(name != null && occurrence != null){
					if(name.equals("Passive")){
						this.socialBehavior[0] = Float.parseFloat(occurrence);
						hasPassive = true;
						continue;
					}
					
					if(name.equals("Social")){
						this.socialBehavior[1] = Float.parseFloat(occurrence);
						hasSocial = true;
						continue;
					}
					
					if(name.equals("Agressive")){
						this.socialBehavior[2] = Float.parseFloat(occurrence);
						hasAgressive = true;
						continue;
					}
				}
			}
		}
		
		if(!hasPassive)
			this.socialBehavior[0] = -50;
		
		if(!hasSocial)
			this.socialBehavior[1] = -25;
		
		if(!hasAgressive)
			this.socialBehavior[2] = -25;
	}

	@Override
	public void setCrowdDiversity(HashMap<String, Node> crowdsHash) {
		this.diversity = new float[3];
		
		boolean hasAge = false;
		boolean hasGender = false;
		boolean hasEthnicity = false;
		
		Node crowdDiversity = null;
		if(crowdsHash != null)
			crowdDiversity = crowdsHash.get(XMLReader.CROWDS_DIVERSITY);
		
		if(crowdDiversity != null){
			LinkedList<String> movementTags = XMLReader.buildList(crowdDiversity);
			LinkedList<HashMap<String, String>> movementInfo = XMLReader.getNodeInformation(crowdDiversity);
			
			for(int i=0; i<movementInfo.size(); i++){
				HashMap<String, String> element = movementInfo.get(i);
				String tag = null;
				for(int j=0; j<movementTags.size(); j++){
					if(element.containsKey(movementTags.get(j))){
						tag = movementTags.get(j);
						movementTags.remove(j);
						break;
					}
				}
				
				if(tag == "age"){
					hasAge = true;
					setAge(element);
					continue;
				}
				
				if(tag == "gender"){
					hasGender = true;
					setGender(element);
					continue;
				}
				
				setEthnicity(element);
				hasEthnicity = true;
			}
		}
		
		if(!hasAge){
			diversity[0] = 25;
			diversity[1] = 15;
		}
		
		if(!hasGender)
			diversity[2] = 0.5f;
		
		if(!hasEthnicity){
			
		}
	}
	
	private void setAge(HashMap<String, String> age){
		if(age.containsKey("mean")){
			diversity[0] = Float.parseFloat(age.get("mean"));
		}else{
			diversity[0] = 25;
		}
		
		if(age.containsKey("std_dv")){
			diversity[1] = Float.parseFloat(age.get("std_dv"));
		}else{
			diversity[1] = 15;
		}
	}
	
	private void setGender(HashMap<String, String> gender){
		if(gender.containsKey("mf_ratio")){
			diversity[2] = Float.parseFloat(gender.get("mf_ratio"));
		}else{
			diversity[2] = 0.5f;
		}
	}
	
	private void setEthnicity(HashMap<String, String> particle){
		
	}
	
	public int getVertexObjectId(String buffer) {
		return crowd.getVertexObjectId(buffer);
	}
	
	public boolean isInitialized(){
		return initialized;
	}
	
	public int getCrowdSize(){
		return crowdSize * crowdSize;
	}
	
	public void addAsset(Asset asset) {
		this.assets.add(asset);
	}
	
	public List<Asset> getAssets() {
		return this.assets;
	}

	public FrameBuffer getFrameBuffer() {
		return frameBuffer;
	}

	public void setFrameBuffer(FrameBuffer frameBuffer) {
		this.frameBuffer = frameBuffer;
	}

	public ShaderObject getCrowdPosShader() {
		return crowdPosShader;
	}

	public void setCrowdPosShader(ShaderObject crowdPosShader) {
		this.crowdPosShader = crowdPosShader;
	}

	public ShaderObject getParticleShader() {
		return particleShader;
	}

	public void setParticleShader(ShaderObject particleShader) {
		this.particleShader = particleShader;
	}


	
	
}
